package org.kingtop.mybatis.util;

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.type.JdbcType;
import org.kingtop.mybatis.persistence.*;
import org.kingtop.mybatis.persistence.metamodel.TableField;
import org.kingtop.mybatis.persistence.metamodel.TableInfo;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 工具类，用于获取指定PO类的所有信息
 *
 * @author 陈金波
 * @date 2018年1月4日 下午3:31:45
 * @Copyright: 2018 kingtop.
 */
public class TableInfoUtil {

	/**
	 * 缓存反射类表信息
	 */
	private static final Map<Class<?>, TableInfo> TABLE_INFO_CACHE = new ConcurrentHashMap<Class<?>, TableInfo>();

	/**
	 * <p>
	 * 根据实体类反射获取表信息
	 * <p>
	 * 
	 * @param clazz
	 *            反射实体类
	 * @return
	 */
	public static TableInfo getTableInfo(Class<?> clazz) {
		return getTableInfo(clazz, null);
	}

	/**
	 * <p>
	 * 根据实体类反射获取表信息
	 * <p>
	 * 
	 * @param clazz
	 *            反射实体类
	 * @param dbType
	 *            数据库类型
	 * @return
	 */
	public static TableInfo getTableInfo(Class<?> clazz, String dbType) {
		TableInfo tableInfo = TABLE_INFO_CACHE.get(clazz);
		if (tableInfo != null) {
			return tableInfo;
		}

		Table table = clazz.getAnnotation(Table.class);
		if (table == null) {
			return null;
		}
		// 获取表的基本信息
		tableInfo = new TableInfo();
		tableInfo.setTableName(table.name());
		tableInfo.setTableNamePrefix(table.prefix());

		List<TableField> fieldList = new ArrayList<>();
		List<Field> fields = getAllClassFields(clazz);
		for (Field field : fields) {
			// 判断是否为主键
			Id id = field.getAnnotation(Id.class);
			if (id != null) {
				TableField pkField = new TableField();
				pkField.setJavaFieldName(field.getName());
				pkField.setJavaType(field.getType());
				pkField.setParams(new HashMap<>(16));
				Column column = field.getAnnotation(Column.class);
				if (column != null) {
					pkField.setDbFieldName(getDbFieldName(column.name(), field.getName()));
					if(JdbcType.OTHER != column.jdbcType()){
						pkField.setJdbcType(column.jdbcType());
					}
					pkField.setOrder(column.order());
				}
				//是否存在sequence自增
				SequenceGenerator sequenceGenerator = field.getAnnotation(SequenceGenerator.class);
				if (sequenceGenerator != null && StringUtils.isNotBlank(sequenceGenerator.name())) {
					pkField.getParams().put(TableField.SEQUENCE_PARAM_NAME, StringUtils.trim(sequenceGenerator.name()));
				}
				//判断是否自定义Redis自增主键辅助信息
				RedisGenerator redisGenerator = field.getAnnotation(RedisGenerator.class);
				if(redisGenerator != null && StringUtils.isNotBlank(redisGenerator.keyName())) {
					pkField.getParams().put(TableField.REDIS_KEY_PARAM_NAME, StringUtils.trim(redisGenerator.keyName()));
				}
				tableInfo.setPkField(pkField);
				tableInfo.setPkKey(id.key());
				continue;
			}
			// 判断是否为字段
			Column column = field.getAnnotation(Column.class);
			if (column != null) {
				TableField tableField = new TableField();
				tableField.setJavaFieldName(field.getName());
				tableField.setJavaType(field.getType());
				tableField.setDbFieldName(getDbFieldName(column.name(), field.getName()));
				tableField.setJdbcType(column.jdbcType());
				tableField.setOrder(column.order());
				fieldList.add(tableField);
			}
		}
		tableInfo.setFieldList(fieldList);

		TABLE_INFO_CACHE.put(clazz, tableInfo);
		return tableInfo;
	}

	/**
	 * 获取数据库字段名，如果没有自定义，则根据驼峰规则，将java字段名称解析成数据库字段
	 * 
	 * @author 陈金波
	 * @date 2018年1月9日 下午9:05:50
	 * @param dbFieldName 数据库字段名称
	 * @param javaFieldName java字段名称
	 * @return String
	 */
	private static String getDbFieldName(String dbFieldName, String javaFieldName) {
		if (StringUtils.isNotBlank(dbFieldName)) {
			return dbFieldName;
		}
		char[] chars = javaFieldName.toCharArray();
		StringBuilder builder = new StringBuilder();
		for (char c : chars) {
			if (Character.isUpperCase(c)) {
				builder.append("_");
			}
			builder.append(Character.toUpperCase(c));
		}
		return builder.toString();
	}

	/**
	 * 获取指定类的所有字段
	 * @param clazz 指定类
	 * @return 所有的字段
	 */
	private static List<Field> getAllClassFields(Class<?> clazz){
		List<Field> fieldList = new ArrayList<>() ;
		//当父类为null的时候说明到达了最上层的父类(Object类).
		while (clazz != null) {
			fieldList.addAll(Arrays.asList(clazz .getDeclaredFields()));
			//得到父类,然后赋给自己
			clazz = clazz.getSuperclass();
		}
		return fieldList;
	}

}
